<?php
declare (strict_types=1);

namespace app\exam\logic;

use think\facade\Cache;

/**
 * 试卷逻辑
 */
class PaperAnswerPost
{

    /**
     * 试卷部分基础信息
     */
    protected $parts;

    /**
     * 试卷试题数
     */
    protected $topic_num = 0;

    /**
     * 作答试题数
     */
    protected $answer_num = 0;

    /**
     * 答对试题数
     */
    protected $right_num = 0;

    /**
     * 用户答案
     */
    protected $answer;

    /**
     * 试卷分数
     */
    protected $score = 0;

    /**
     * 用户得分
     */
    protected $user_score = 0;

    /**
     * 唯一标识
     */
    protected $unique_code;

    /**
     * redis实例
     */
    protected $redis;

    /**
     * 主观题
     */
    protected $subjective = null;

    /**
     * 构造函数
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date 2021-04-21
     *
     * @param array $parts  [description]
     * @param array $answer [description]
     */
    public function __construct(string $code, array $parts, array $answer)
    {
        // redis缓存
        $this->redis       = Cache::store('redis')->handler();
        $this->unique_code = $code;
        // 部分处理
        $this->parts = $parts;
        $this->parseParts();
        // 答案处理
        $this->answer = $answer;
        $this->parseAnswer();
    }

    /**
     * 解析parts
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date 2021-04-21
     *
     * @return [type] [description]
     */
    public function parseParts()
    {
        // 试卷部分缓存数据
        // $paper_parts = $this->redis->hGet('paper_unique_parts', $this->unique_code);
        // $score 		 = $this->redis->hGet('paper_unique_score', $this->unique_code);
        // if ($paper_parts && $score) {
        // 	$this->parts = unserialize($paper_parts);
        // 	$this->score = unserialize($score);
        // 	return;
        // }

        $parts = [];
        foreach ($this->parts as $part) {
            $value = [];
            foreach ($part['value'] as $val) {
                // 获取试题
                /** @var Topic $topicLogic */
                $topicLogic = app(Topic::class);
                $topic_info = $topicLogic->getTopicInfoById((string)$val['topic_id']);
                // 获取试题
                $topic_info = current($topic_info);
                // 定义试题part_id
                $topic_info->part_id = $part['id'];
                // 存在主观题（简答题）
                $topic_info->subjective = $topic_info->question_type == 8 ? 1 : 0;
                if (null === $this->subjective) $this->subjective = $topic_info->subjective ? true : null;

                if (1 == $topic_info->topic_level) {
                    $this->topic_num          += 1;
                    $topic_info->answer_data  = null;
                    $topic_info->answer_right = 0;

                    // $val['score'] = $topic_info->question_type == 5 ? $val['score']*count($topic_info->options) : $val['score'];

                } else if (2 == $topic_info->topic_level) {
                    $this->topic_num += 1;
                    foreach ($topic_info->topics as $option_topic) {
                        // $this->topic_num 		   += 1;
                        $option_topic->answer_data  = null;
                        $option_topic->answer_right = 0;
                        $option_topic->part_id      = $part['id'];

                        $topic_key                = array_search($option_topic->id, array_column($val['sub_topics'], 'topic_id'));
                        $option_topic->score      = $val['sub_topics'][$topic_key]['score'];
                        $option_topic->score_miss = $val['sub_topics'][$topic_key]['score_miss'];
                        // 存在主观题（简答题）
                        $option_topic->subjective = $option_topic->question_type == 8 ? 1 : 0;
                        $option_topic->subjective && $topic_info->subjective = 1;
                        if (null === $this->subjective) $this->subjective = $option_topic->subjective ? true : null;
                    }
                }
                $this->score += $val['score'];
                // 重定义value
                $value[] = array_merge(json_decode(json_encode($val), true), json_decode(json_encode($topic_info), true));
            }
            // 重定义part
            $part['value'] = $value;
            // 重定义parts
            $parts[] = $part;
        }
        $this->parts = $parts;

        // $this->redis->hSet('paper_unique_parts', $this->unique_code, serialize($this->parts));
        // $this->redis->hSet('paper_unique_score', $this->unique_code, serialize($this->score));
    }

    /**
     * 解析答案
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date 2021-04-21
     *
     * @return [type] [description]
     */
    public function parseAnswer()
    {
        foreach ($this->answer as $ad) {
            $num = count($ad["data"]);
            // $this->answer_num += $num;	// 关闭小题答题统计
            if (!$num) continue;
            foreach ($ad["data"] as $opt) {
                if (!count($opt["answer"])) continue;
                $key       = $ad["part_id"] . '-' . $opt["topic_level"] . '-' . $opt["topic_id"];
                $new[$key] = [
                    'part_id'       => $ad["part_id"],
                    'topic_level'   => $opt["topic_level"],
                    'topic_id'      => $opt["topic_id"],
                    'question_type' => $opt["question_type"],
                    'answer_data'   => $opt["answer"],
                ];
                if ($opt['topic_level'] == 1) {
                    $this->addTopicAnswerNum(intval($opt['topic_id']));
                }
            }
        }
        $this->answer = $new ?? [];
    }

    /**
     * 获取parts
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date 2021-04-21
     *
     * @return [type] [description]
     */
    public function getParts()
    {
        return $this->parts;
    }

    /**
     * 获取试卷试题数
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date 2021-06-22
     *
     * @return [type] [description]
     */
    public function getTopicNum()
    {
        return $this->topic_num;
    }

    /**
     * 获取是否存在主观题
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date 2021-04-23
     *
     * @return [type] [description]
     */
    public function getSubjective()
    {
        if (null === $this->subjective) {
            $this->subjective = false;
        }
        return $this->subjective;
    }

    /**
     * 获取答案
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date 2021-04-21
     *
     * @return [type] [description]
     */
    public function getAnswer()
    {
        return $this->answer;
    }

    /**
     * 获取作答试题数
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date 2021-06-22
     *
     * @return [type] [description]
     */
    public function getAnswerNum()
    {
        return $this->answer_num;
    }

    /**
     * 获取答对试题数
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date 2021-06-22
     *
     * @return [type] [description]
     */
    public function getRightNum()
    {
        return $this->right_num;
    }

    /**
     * 获取试卷分数
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date 2021-04-30
     *
     * @return [type] [description]
     */
    public function getScore()
    {
        return $this->score;
    }

    /**
     * 获取用户分数
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date 2021-04-30
     *
     * @return [type] [description]
     */
    public function getUserScore()
    {
        return $this->user_score;
    }

    public function handle()
    {
        $parts = $this->parts;

        foreach ($parts as &$part) {
            foreach ($part['value'] as &$val) {
                if (1 == $val['topic_level']) {
                    $val = $this->handleTopicAnswer($val);

                    $val['answer_data'] && $this->answer_num += 1;    // 大题统计
                    $val['answer_right'] && $this->right_num += 1;    // 大题统计

                } else if (2 == $val['topic_level']) {
                    $answer_right  = 1;
                    $answer_status = 0;
                    foreach ($val['topics'] as &$option_topic) {
                        $option_topic = $this->handleTopicAnswer($option_topic);
                        if (!$option_topic['answer_right']) $answer_right = 0;
                        if ($option_topic['answer_data']) $answer_status = 1;
                    }

                    $answer_status && $this->answer_num += 1;    // 大题统计
                    $answer_right && $this->right_num += 1;        // 大题统计

                    $val['answer_right'] = $answer_right;// 用户解析页面图标显示
                    $val['user_score']   = array_sum(array_values(array_column($val['topics'], 'user_score')));
                    unset($option_topic);
                }
            }
            unset($val);
        }
        unset($part);
        $this->parts = $parts;
    }

    /**
     * 处理试题答案
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date 2021-03-25
     *
     * @param  [type] $data    [description]
     * @param  [type] $epar_id [description]
     * @return [type]          [description]
     */
    public function handleTopicAnswer($data)
    {
        $answer = $this->answer;
        if (!$answer) {
            $data['answer_right'] = $data['user_score'] = 0;
            return $data;
        }

        $answer_key = $data['part_id'] . '-' . $data['topic_level'] . '-' . $data['id'];

        if (isset($answer[$answer_key])) {
            // 用户答案
            $data['answer_data'] = $answer[$answer_key]['answer_data'];
            // 获取用户答案正确及得分
            $right_score = $this->handleAnswerRightAndScore($data);
            // 试题分
            $answer[$answer_key]['topic_score'] = $data['score'];
            $data['answer_right']               = $right_score['right'];
            $data['user_score']                 = $right_score['score'];
            $answer[$answer_key]['user_score']  = $right_score['score'];
            // 试题大题正确错误
            $answer[$answer_key]['answer_right'] = $right_score['right'];
            // if ( $right_score['right'] ) $this->right_num += 1;	// 关闭小题统计
            $this->user_score += $right_score['score'];
        } else {
            $data['answer_right'] = $data['user_score'] = 0;
        }
        $this->answer = $answer;
        return $data;
    }

    /**
     * 处理答案正确及得分
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date 2021-03-25
     *
     * @param  [type] $data [description]
     * @return [type]       [description]
     */
    public function handleAnswerRightAndScore($data)
    {
        //
        $right = $score = 0;
        switch ($data['question_type']) {
            case 1:    // 单选
            case 2:    // 判断
            case 3:    // 多选
            case 4:    // 不定项
                if (!$data['options']) {
                    break;
                }

                $right_data = self::arrayFilterFieldValue($data['options'], 'is_right', 1);

                if (count($data['answer_data'])) {
                    $rads = array_values(array_column($right_data, 'id'));
                    $ads  = array_values($data['answer_data']);
                    sort($rads);
                    sort($ads);
                    // 存在错选
                    $wrong = array_diff($ads, $rads);
                    if ($wrong) break;
                    // 得分情况
                    $count = count(array_intersect($ads, $rads));
                    $right = $count == count($rads) ? 1 : 0;
                    $score = $right ? $data['score'] : ($count ? $data['score_miss'] : 0);
                } else {
                    $right = $score = 0;
                }

                break;

            case 5:    // 填空题（按题得分，后期按空得分）

                if (!$data['options']) {
                    break;
                }
                // 按题得分
                // $right = $this->handleCompletion1($data);
                // $score = $right ? $data['score'] : 0;
                // 按空得分
                $count = $this->handleCompletion2($data);
                $right = count($data['options']) == $count ? 1 : 0;
                $score = $count * $data['score'] / count($data['options']);
                break;

            default:

                break;
        }

        return ['right' => $right, 'score' => $score];
    }

    /**
     * 填空题按题得分
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date 2021-04-23
     *
     * @param  [type] $data [description]
     * @return [type]       [description]
     */
    public function handleCompletion1($data)
    {
        // 填空选项随机
        $answer_rand = $data['completion_position'];

        $right = 0;

        if (count($data['answer_data'])) {

            $opt_arr = $data['options'];

            if ($answer_rand) {
                // 无序答案
                $right = 1;
                foreach ($data['answer_data'] as $k => $v) {
                    foreach ($opt_arr as $opk => &$opt) {
                        $in = array_search($v, $opt['values']);
                        // 存在正确答案，删除已确定正确答案数组元素
                        if (false !== $in) {
                            $right = 1;
                            unset($opt_arr[$opk]);
                            break;
                        } else {
                            $right = 0;
                        }
                    }
                    // 存在错误答案，退出程序
                    if (!$right) break;
                }
            } else {
                // 有序答案
                foreach ($opt_arr as $opk => $opt) {
                    $new_opt[$opt['number']] = $opt['values'];
                }

                $right_com = array_map(function ($asd, $opt) {
                    return in_array($asd, $opt) * 1;
                }, $data['answer_data'], $new_opt);

                $right = in_array(0, $right_com) ? 0 : 1;
            }
        }

        return $right;
    }

    /**
     * 填空题按空得分
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date 2021-04-23
     *
     * @param  [type] $data [description]
     * @return [type]       [description]
     */
    public function handleCompletion2($data)
    {
        // 填空选项随机
        $answer_rand = $data['completion_position'];

        $right_num = 0;

        if (!count($data['answer_data'])) return $right_num;

        $opt_arr = $data['options'];

        if ($answer_rand) {
            // 无序答案
            $right_num = 0;
            foreach ($data['answer_data'] as $k => $v) {
                // 当前答案是否在正确答案中
                foreach ($opt_arr as $opk => &$opt) {
                    $in = array_search($v, $opt['values']);
                    // 存在正确答案，删除已确定正确答案数组元素
                    if (false !== $in) {
                        $right_num += 1;
                        unset($opt_arr[$opk]);
                        break;
                    }
                }
            }
        } else {
            // 有序答案
            foreach ($opt_arr as $opk => $opt) {
                $new_opt[$opt['number']] = $opt['values'];
            }

            $right_com = array_map(function ($asd, $opt) {
                return in_array($asd, $opt) * 1;
            }, $data['answer_data'], $new_opt);

            $right_num = 0;
            foreach ($right_com as $v) {
                $right_num += $v;
            }
        }

        return $right_num;
    }

    /**
     * 获取符合字段和字段值的数组集合
     *
     * @param array  $data  待过滤数组
     * @param string $field 要查找的字段
     * @param        $value 要查找的字段值
     * @return array返回所有符合要求的数组集合
     */
    public static function arrayFilterFieldValue(array $data, string $field, $value)
    {
        $data = array_filter($data, function ($row) use ($field, $value) {
            if (isset($row[$field])) {
                return $row[$field] == $value;
            }
        });

        return $data;
    }

    /**
     * 增加试题作答次数
     * Author: 亓官雨树 <lucky.max@foxmail.com>
     * Date: 22/02/15
     *
     * @param int $topicId
     */
    private function addTopicAnswerNum(int $topicId)
    {
        /** @var Topic $topicLogic */
        $topicLogic = app(Topic::class);
        $topicLogic->addTopicAnswerNum($topicId);
    }

}